{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "true"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### pry-docの読み込み\n",
    "require \"/root/git_jupyter_notebook/Ruby/vendor/bundle/ruby/2.3.0/gems/pry-doc-0.10.0/lib/pry-doc\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# Hashクラス\n",
    "\n",
    "* ハッシュ\n",
    "    * 連想配列とも呼ばれる\n",
    "    * 配列のインデックスにあたるキーとして任意のRubyオブジェクトを利用できる\n",
    "        * キーにはシンボルがよく使われるので、参考書と違ってシンボルで書いていく\n",
    "    * Hashクラスのオブジェクトとして生成される\n",
    "    * Ruby1.9よりハッシュに含まれる要素の順序が、キーが追加された順序で保持されるようになった"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Hash, JSON::Ext::Generator::GeneratorMethods::Hash, Enumerable, Object, PP::ObjectMixin, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Hash.ancestors"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## ハッシュの生成"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "キーには任意のRubyオブジェクトが使える\n",
      "{1=>\"num1\", 2=>\"num2\"}\n",
      "{\"apple\"=>\"fruit\", \"coffee\"=>\"drink\"}\n",
      "{:apple=>\"fruit\", :coffee=>\"drink\"}\n",
      "\n",
      "ハッシュの生成方法\n",
      "{:apple=>\"fruit\", :coffee=>\"drink\"}\n",
      "{:apple=>\"fruit\", :coffee=>\"drink\"}\n",
      "{:apple=>\"fruit\", :coffee=>\"drink\"}\n",
      "\n",
      "newで生成\n",
      "{}\n",
      "初期値を設定\n",
      "{}\n",
      "init\n",
      "{:fuga=>3}\n",
      "\n",
      "ブロックで初期値を設定\n",
      "init\n",
      "\n",
      "初期値の参照と変更\n",
      "init\n",
      "change\n",
      "\n",
      "#<Proc:0x007f021afc0f58@(pry):796 (lambda)>\n",
      "{}\n",
      "hogehoge\n"
     ]
    }
   ],
   "source": [
    "puts \"キーには任意のRubyオブジェクトが使える\"\n",
    "puts a = {1 => \"num1\", 2 => \"num2\" }\n",
    "puts b = {\"apple\" => \"fruit\", \"coffee\" => \"drink\" }\n",
    "puts c = {:apple => \"fruit\", :coffee => \"drink\" }\n",
    "puts \"\"\n",
    "\n",
    "puts \"ハッシュの生成方法\"\n",
    "puts a = {:apple => \"fruit\", :coffee => \"drink\"}\n",
    "puts b = {apple: \"fruit\", coffee: \"drink\"}\n",
    "puts c = Hash[:apple, \"fruit\", :coffee, \"drink\"]\n",
    "puts \"\"\n",
    "\n",
    "puts \"newで生成\"\n",
    "puts a = Hash.new\n",
    "puts \"初期値を設定\"\n",
    "puts b = Hash.new(\"init\")\n",
    "puts b[:hoge]\n",
    "b[:fuga] = 3\n",
    "puts b\n",
    "puts \"\"\n",
    "\n",
    "puts \"ブロックで初期値を設定\"\n",
    "a = Hash.new{|hash,key| hash[key] = \"init\"}\n",
    "puts a[:haga]\n",
    "puts \"\"\n",
    "\n",
    "puts \"初期値の参照と変更\"\n",
    "a = Hash.new(\"init\")\n",
    "puts a.default\n",
    "a.default = \"change\"\n",
    "puts a.default\n",
    "puts \"\"\n",
    "\n",
    "a.default_proc = ->(hash, key) { hash[key] = key * 2 }\n",
    "puts a.default_proc.inspect\n",
    "puts a\n",
    "puts a[\"hoge\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## ハッシュのキーや値を取得\n",
    "\n",
    "* メソッド\n",
    "    * []\n",
    "        * 指定されたキーに対応する値を返す\n",
    "    * keys\n",
    "        * ハッシュのすべてのキーを配列で返す\n",
    "    * values\n",
    "        * ハッシュのすべての値を配列で返す\n",
    "    * values_at\n",
    "        * 指定されたキーに対応する値を返す\n",
    "    * fetch\n",
    "        * 与えられたキーに対する値を返す\n",
    "        * キーが無ければ2番目の引数の値、もしくはブロックの評価結果を返す\n",
    "    * select\n",
    "        * キーと値の組み合わせでブロックを評価\n",
    "        * 結果が真となる組み合わせのみを含むハッシュを返す\n",
    "            * Ruby1.8以前は配列を返していた"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{:apple=>\"fruit\", :coffee=>\"drink\", :tomato=>\"vegetable\"}\n",
      "fruit\n",
      "[:apple, :coffee, :tomato]\n",
      "[\"fruit\", \"drink\", \"vegetable\"]\n",
      "[\"drink\", \"vegetable\"]\n",
      "noodle\n",
      "noodle\n",
      "{:apple=>\"fruit\"}\n"
     ]
    }
   ],
   "source": [
    "puts a = {apple: \"fruit\", coffee: \"drink\", tomato: \"vegetable\"}\n",
    "puts a[:apple]\n",
    "puts a.keys\n",
    "puts a.values\n",
    "puts a.values_at(:coffee, :tomato)\n",
    "puts a.fetch(:pasta, \"noodle\")\n",
    "puts a.fetch(:pasta){|h| \"noodle\" }\n",
    "puts a.select{|key, value| key =~ /le$/ }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## ハッシュを変更する\n",
    "\n",
    "* メソッド\n",
    "    * []=\n",
    "        * キーに対応する値を変更する\n",
    "        * キーが無ければ新たに作成する\n",
    "    * delete\n",
    "        * 指定されたキーに対応する値を取り除く\n",
    "        * キーが無ければnilを返す\n",
    "        * ブロックがあれば指定したキーが存在しないときにブロックを返す\n",
    "    * reject\n",
    "        * ブロックを評価した結果が真になる値を取り除いたハッシュを生成して返す\n",
    "        * <font color=\"red\">元のオブジェクトは変更されない</font>\n",
    "    * delete_if\n",
    "        * ブロックを評価した結果が真になる値を取り除く\n",
    "        * <font color=\"red\">破壊的メソッド</font>\n",
    "        * reject!と同じ\n",
    "    * replace\n",
    "        * 引数で与えられたハッシュで自分自身を置き換える\n",
    "        * オブジェクトIDは変わらない\n",
    "    * shift\n",
    "        * ハッシュからキーと値の組み合わせを1つ取り除きその組み合わせを<font color=\"red\">配列で返す</font>\n",
    "        * 必ずしも先頭が取り除かれるとは限らない？\n",
    "    * merge\n",
    "        * 自分自身と引数で指定されたハッシュを統合(マージ)した<font color=\"red\">新しいハッシュオブジェクトを返す</font>\n",
    "        * デフォルトの値は自分自身の設定が引き継がれる\n",
    "        * ブロックがあればキーと自分自身、指定されたハッシュの値が渡されてブロックの評価結果が新しいハッシュの値となる\n",
    "    * update\n",
    "        * 自分自身と引数で指定されたハッシュを統合(マージ)する\n",
    "        * <font color=\"red\">破壊的メソッド</font>\n",
    "        * merge!と同じ？        \n",
    "    * invert\n",
    "        * キーと値を逆にしたハッシュを返す\n",
    "        * 元のハッシュは変更されない\n",
    "        * 値が重複していると不安定になる\n",
    "    * clear\n",
    "        * ハッシュを空にする"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{:fruit=>\"apple\", :drink=>\"coffee\", :vegetable=>\"tomato\"}\n",
      "{:fruit=>\"banana\", :drink=>\"coffee\", :vegetable=>\"tomato\", :noodle=>\"pasta\"}\n",
      "\n",
      "delete\n",
      "coffee\n",
      "{:fruit=>\"banana\", :vegetable=>\"tomato\", :noodle=>\"pasta\"}\n",
      "nil\n",
      "key not fount.\n",
      "\n",
      "reject\n",
      "{:fruit=>\"banana\", :vegetable=>\"tomato\", :noodle=>\"pasta\"}\n",
      "{:vegetable=>\"tomato\", :noodle=>\"pasta\"}\n",
      "{:fruit=>\"banana\", :noodle=>\"pasta\"}\n",
      "{:fruit=>\"banana\", :vegetable=>\"tomato\", :noodle=>\"pasta\"}\n",
      "\n",
      "delete_if\n",
      "{:fruit=>\"banana\", :vegetable=>\"tomato\", :noodle=>\"pasta\"}\n",
      "{:vegetable=>\"tomato\", :noodle=>\"pasta\"}\n",
      "{:vegetable=>\"tomato\", :noodle=>\"pasta\"}\n",
      "\n",
      "replace\n",
      "{:vegetable=>\"tomato\", :noodle=>\"pasta\"}\n",
      "{:fruit=>\"orange\", :drink=>\"tea\", :vegetable=>\"cabbage\"}\n",
      "\n",
      "shift\n",
      "{:fruit=>\"orange\", :drink=>\"tea\", :vegetable=>\"cabbage\"}\n",
      "[:fruit, \"orange\"]\n",
      "{:drink=>\"tea\", :vegetable=>\"cabbage\"}\n",
      "\n",
      "merge\n",
      "{:drink=>\"tea\", :vegetable=>\"cabbage\"}\n",
      "{:drink=>\"tea\", :vegetable=>\"cabbage\", :sweets=>\"candy\"}\n",
      "{:drink=>\"tea\", :vegetable=>\"cabbage\", :sweets=>\"candy\"}\n",
      "{:drink=>\"tea\", :vegetable=>\"cabbage\"}\n",
      "\n",
      "update\n",
      "{:drink=>\"tea\", :vegetable=>\"cabbage\"}\n",
      "{:drink=>\"tea\", :vegetable=>\"cabbage\", :sweets=>\"candy\"}\n",
      "{:drink=>\"tea\", :vegetable=>\"cabbage\", :sweets=>\"candy\"}\n",
      "\n",
      "invert\n",
      "{:drink=>\"tea\", :vegetable=>\"cabbage\", :sweets=>\"candy\"}\n",
      "{\"tea\"=>:drink, \"cabbage\"=>:vegetable, \"candy\"=>:sweets}\n",
      "{:drink=>\"tea\", :vegetable=>\"cabbage\", :sweets=>\"candy\"}\n",
      "\n",
      "clear\n",
      "{}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "puts a = {fruit: \"apple\", drink: \"coffee\", vegetable: \"tomato\"}\n",
    "a[:fruit] = \"banana\"\n",
    "a[:noodle] = \"pasta\"\n",
    "puts a\n",
    "puts \"\"\n",
    "\n",
    "puts \"delete\"\n",
    "puts a.delete(:drink)\n",
    "puts a\n",
    "p a.delete(:drink)\n",
    "puts a.delete(:drink){|h| \"key not fount.\"}\n",
    "puts \"\"\n",
    "\n",
    "puts \"reject\"\n",
    "puts a\n",
    "puts a.reject{|key,value| value == \"banana\"}\n",
    "puts a.reject{|key,value| key == :vegetable}\n",
    "puts a\n",
    "puts \"\"\n",
    "\n",
    "puts \"delete_if\"\n",
    "puts a\n",
    "puts a.delete_if{|key,value| value == \"banana\"}\n",
    "puts a\n",
    "puts \"\"\n",
    "\n",
    "puts \"replace\"\n",
    "puts a\n",
    "a.replace({fruit: \"orange\", drink: \"tea\", vegetable: \"cabbage\"})\n",
    "puts a\n",
    "puts \"\"\n",
    "\n",
    "puts \"shift\"\n",
    "puts a\n",
    "puts a.shift\n",
    "puts a\n",
    "puts \"\"\n",
    "\n",
    "puts \"merge\"\n",
    "puts a\n",
    "puts a.merge({sweets: \"candy\"})\n",
    "puts a.merge({sweets: \"candy\"}){|key,self_value,other_value|}\n",
    "puts a\n",
    "puts \"\"\n",
    "\n",
    "puts \"update\"\n",
    "puts a\n",
    "puts a.update({sweets: \"candy\"})\n",
    "puts a\n",
    "puts \"\"\n",
    "\n",
    "puts \"invert\"\n",
    "puts a\n",
    "puts a.invert\n",
    "puts a\n",
    "puts \"\"\n",
    "\n",
    "puts \"clear\"\n",
    "puts a.clear\n",
    "puts \"\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## ハッシュを調べる\n",
    "\n",
    "* メソッド\n",
    "    * length\n",
    "        * ハッシュの長さを調べる\n",
    "    * size\n",
    "        * lengthと同じ\n",
    "    * has_key?\n",
    "        * ハッシュにキーが存在する場合にtrueを返す\n",
    "    * include?\n",
    "        * ハッシュにキーが存在する場合にtrueを返す\n",
    "    * key?\n",
    "        * ハッシュにキーが存在する場合にtrueを返す\n",
    "    * member?\n",
    "        * ハッシュにキーが存在する場合にtrueを返す\n",
    "    * has_value?\n",
    "        * ハッシュに値が存在する場合にtrueを返す\n",
    "    * value?\n",
    "        * ハッシュに値が存在する場合にtrueを返す\n",
    "    * empty?\n",
    "        * ハッシュが空場合にtrueを返す\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{:fruit=>\"apple\", :drink=>\"coffee\", :vegetable=>\"tomato\"}\n",
      "3\n",
      "3\n",
      "\n",
      "true\n",
      "true\n",
      "true\n",
      "true\n",
      "\n",
      "true\n",
      "true\n",
      "\n",
      "false\n",
      "true\n"
     ]
    }
   ],
   "source": [
    "puts a = {fruit: \"apple\", drink: \"coffee\", vegetable: \"tomato\"}\n",
    "puts a.length\n",
    "puts a.size\n",
    "puts \"\"\n",
    "\n",
    "puts a.has_key?(:fruit)\n",
    "puts a.key?(:fruit)\n",
    "puts a.include?(:fruit)\n",
    "puts a.member?(:fruit)\n",
    "puts \"\"\n",
    "\n",
    "puts a.has_value?(\"apple\")\n",
    "puts a.value?(\"tomato\")\n",
    "puts \"\"\n",
    "\n",
    "puts a.empty?\n",
    "a.clear\n",
    "puts a.empty?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## ハッシュを使った繰り返し\n",
    "\n",
    "* メソッド\n",
    "    * each\n",
    "        * 与えられたブロックにキーと値を渡して評価する\n",
    "    * each_pair\n",
    "        * 与えられたブロックにキーと値を渡して評価する\n",
    "    * each_key\n",
    "        * 与えられたブロックにキーを渡して評価する\n",
    "    * each_value\n",
    "        * 与えられたブロックに値を渡して評価する"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 118,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{:fruit=>\"apple\", :drink=>\"coffee\", :vegetable=>\"tomato\"}\n",
      "fruit: apple\n",
      "drink: coffee\n",
      "vegetable: tomato\n",
      "\n",
      "fruit: apple\n",
      "drink: coffee\n",
      "vegetable: tomato\n",
      "\n",
      "fruit\n",
      "drink\n",
      "vegetable\n",
      "\n",
      "apple\n",
      "coffee\n",
      "tomato\n",
      "\n"
     ]
    }
   ],
   "source": [
    "puts a = {fruit: \"apple\", drink: \"coffee\", vegetable: \"tomato\"}\n",
    "a.each{|key, value| puts \"#{key}: #{value}\"}\n",
    "puts \"\"\n",
    "a.each_pair{|key, value| puts \"#{key}: #{value}\"}\n",
    "puts \"\"\n",
    "a.each_key{|key| puts key}\n",
    "puts \"\"\n",
    "a.each_value{|value| puts value}\n",
    "puts \"\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## ハッシュをソートする\n",
    "\n",
    "* sort\n",
    "    * ハッシュとキーの組み合わせを配列に変換し、それをソートした結果を返す\n",
    "        * ハッシュ自身をソートするわけでは無い"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 122,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{:vegetable=>\"tomato\", :fruit=>\"apple\", :drink=>\"coffee\"}\n",
      "keyでソート\n",
      "[[:drink, \"coffee\"], [:fruit, \"apple\"], [:vegetable, \"tomato\"]]\n",
      "[[:drink, \"coffee\"], [:fruit, \"apple\"], [:vegetable, \"tomato\"]]\n",
      "値でソート\n",
      "[[:fruit, \"apple\"], [:drink, \"coffee\"], [:vegetable, \"tomato\"]]\n"
     ]
    }
   ],
   "source": [
    "puts a = {vegetable: \"tomato\", fruit: \"apple\", drink: \"coffee\"}\n",
    "puts \"keyでソート\"\n",
    "puts a.sort\n",
    "puts a.sort{|a,b| a[0] <=> b[0]}\n",
    "puts \"値でソート\"\n",
    "puts a.sort{|a,b| a[1] <=> b[1]}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## ハッシュを変換する\n",
    "\n",
    "* メソッド\n",
    "    * to_a\n",
    "        * ハッシュを配列に変換する"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 123,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{:fruit=>\"apple\", :drink=>\"coffee\", :vegetable=>\"tomato\"}\n",
      "[[:fruit, \"apple\"], [:drink, \"coffee\"], [:vegetable, \"tomato\"]]\n"
     ]
    }
   ],
   "source": [
    "puts a = {fruit: \"apple\", drink: \"coffee\", vegetable: \"tomato\"}\n",
    "puts a.to_a"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Ruby 2.3.1",
   "language": "ruby",
   "name": "ruby"
  },
  "language_info": {
   "file_extension": ".rb",
   "mimetype": "application/x-ruby",
   "name": "ruby",
   "version": "2.3.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
